/**
 * @fileoverview Javascript Logger (in the spirit of log4j) This library is
 *               designed to make the writing and debugging of javascript code
 *               easier, by allowing the programmer to perform debug or log
 *               output at any place in their code. This supports the concept of
 *               different levels of logging (debug < info < warn < error <
 *               fatal << none) as well as different log outputs. Three log
 *               outputs are included, but you can add your own. The included
 *               log outputs are {@link Log#writeLogger},
 *               {@link Log#alertLogger}, and {@link Log#popupLogger}. For
 *               debugging on Safari, the log ouput {@link Log#consoleLogger} is
 *               also included. To turn off debugging but still leave the logger
 *               calls in your script, use the log level {@link Log#NONE}.
 * 
 * Example usage:
 * 
 * <pre>
 * &lt;html&gt;
 *  &lt;head&gt;
 *      &lt;script src=&quot;log4js.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
 *  &lt;/head&gt;
 *  &lt;body&gt;
 *     Log4JS test...&lt;hr/&gt;
 *     &lt;script&gt;
 *        // Setup log objects
 *        //
 *        //  log object of priority debug and the popup logger
 *        var log = new Log(Log.DEBUG, Log.popupLogger);
 *        //  log object of priority warn and the alert logger
 *        var log2 = new Log(Log.WARN, Log.alertLogger);
 *        //  log object of priority debug and the console logger (Safari)
 *        var log3 = new Log(Log.DEBUG, Log.consoleLogger);
 *        log.debug('foo1');     // will popup a new window and log 'foo'
 *        log.warn('bar1');      // will add a new 'bar' message to the popup
 *        log2.debug('foo2');    // will do nothing (Log object's priority threshold is WARN)
 *        log2.warn('bar2');     // will display a javascript alert with the string 'bar'
 *        log3.debug('foo3');    // will log message to Safari console or existing popup
 *        log3.warn('bar3');     // same
 * 	      log.info(Log.dumpObject(new Array('apple','pear','orange','banana')));
 *     &lt;/script&gt;
 *  &lt;/body&gt;
 * &lt;/html&gt;
 * </pre>
 * 
 * @author Marcus R Breese mailto:mbreese@users.sourceforge.net
 * @license Apache License 2.0
 * @version 0.31
 * 
 * <pre>
 * ************************************************************
 * Copyright 2005 Fourspaces Consulting, LLC
 * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * http://www.apache.org/licenses/LICENSE-2.0 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an &quot;AS IS&quot; BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License
 * ************************************************************
 * Changelog:
 * 0.31 Bug fix (resizeable should be resizable - Darryl Lyons)
 * 0.3  Migrated to SF.net SVN repository - test cleanups
 * 0.2 	- Added consoleLogger for Safari
 * 		- Changed popupLogger so that it only notifies once (or twice)
 * 	  that a popup blocker is active.	
 * 	- Added Log.NONE level for silencing all logging
 * </pre>
 */

/**
 * Create a new logger
 * 
 * @constructor
 * @class The main Log class. Create a new instance of this class to send all
 *        logging events.
 * @param level
 *            The cut-off logger level. You can adjust this level in the
 *            constructor and leave all other logging events in place. Defaults
 *            to {@link Log#WARN}.
 * @param logger
 *            The logger to use. The logger is a function that accepts the
 *            logging events and informs the user or developer. Defaults to
 *            {@link Log#writeLogger}.
 */
function Log(level, logger, prefix) {
	var _currentLevel = Log.WARN;
	var _logger = Log.writeLogger; // default to write Logger
	var _prefix = false;
	/**
	 * Sets the current logger prefix
	 * 
	 * @param {String}
	 *            prefix This prefix will be prepended to all messages.
	 */
	this.setPrefix = function(pre) {
		if (pre != 'undefined') {
			_prefix = pre;
		} else {
			_prefix = false;
		}
	}
	/**
	 * Sets the current logger function
	 * 
	 * @param logger
	 *            The function that will be called when a log event needs to be
	 *            displayed
	 */
	this.setLogger = function(logger) {
		if (logger != 'undefined') {
			_logger = logger;
		}
	}

	/**
	 * Sets the current threshold log level for this Log instance. Only events
	 * that have a priority of this level or greater are logged.
	 * 
	 * @param level
	 *            The new threshold priority level for logging events. This can
	 *            be one of the static members {@link Log#DEBUG},
	 *            {@link Log#INFO}, {@link Log#WARN}, {@link Log#ERROR},
	 *            {@link Log#FATAL}, {@link Log#NONE}, or it can be one of the
	 *            strings ["debug", "info", "warn", "error", "fatal", "none"].
	 */
	this.setLevel = function(level) {
		if (level != 'undefined' && typeof level == 'number') {
			_currentLevel = level;
		} else if (level != 'undefined') {
			if (level == 'debug') {
				_currentLevel = Log.DEBUG;
			} else if (level == 'info') {
				_currentLevel = Log.INFO;
			} else if (level == 'error') {
				_currentLevel = Log.ERROR;
			} else if (level == 'fatal') {
				_currentLevel = Log.FATAL;
			} else if (level == 'warn') {
				_currentLevel = Log.WARN;
			} else {
				_currentLevel = Log.NONE;
			}
		}
	}

	/**
	 * Gets the current prefix
	 * 
	 * @return current prefix
	 */

	this.getPrefix = function() {
		return _prefix;
	}

	/**
	 * Gets the current event logger function
	 * 
	 * @return current logger
	 */

	this.getLogger = function() {
		return _logger;
	}

	/**
	 * Gets the current threshold priority level
	 * 
	 * @return current level
	 */

	this.getLevel = function() {
		return _currentLevel;
	}

	if (level != 'undefined') {
		this.setLevel(level);
	}
	if (logger != 'undefined') {
		this.setLogger(logger);
	}
	if (prefix != 'undefined') {
		this.setPrefix(prefix);
	}
}

Log.on = true;

/**
 * Log an event with priority of "debug"
 * 
 * @param s
 *            the log message
 */
Log.prototype.debug = function(s) {
	if (this.getLevel() <= Log.DEBUG) {
		this._log(s, "DEBUG", this);
	}
}
/**
 * Log an event with priority of "info"
 * 
 * @param s
 *            the log message
 */
Log.prototype.info = function(s) {
	if (this.getLevel() <= Log.INFO) {
		this._log(s, "INFO", this);
	}
}
/**
 * Log an event with priority of "warn"
 * 
 * @param s
 *            the log message
 */
Log.prototype.warn = function(s) {
	if (this.getLevel() <= Log.WARN) {
		this._log(s, "WARN", this);
	}
}
/**
 * Log an event with priority of "error"
 * 
 * @param s
 *            the log message
 */
Log.prototype.error = function(s) {
	if (this.getLevel() <= Log.ERROR) {
		this._log(s, "ERROR", this);
	}
}
/**
 * Log an event with priority of "fatal"
 * 
 * @param s
 *            the log message
 */
Log.prototype.fatal = function(s) {
	if (this.getLevel() <= Log.FATAL) {
		this._log(s, "FATAL", this);
	}
}

/**
 * _log is the function that actually calling the configured logger function. It
 * is possible that this function could be extended to allow for more than one
 * logger.
 * 
 * This method is used by {@link Log#debug}, {@link Log#info},
 * {@link Log#warn}, {@link Log#error}, and {@link Log#fatal}
 * 
 * @private
 * @param {String}
 *            msg The message to display
 * @param level
 *            The priority level of this log event
 * @param {Log}
 *            obj The originating {@link Log} object.
 */
Log.prototype._log = function(msg, level, obj) {
	if (!Log.on) {
		return;
	}
	if (this.getPrefix()) {
		this.getLogger()(this.getPrefix() + " - " + msg, level, obj);
	} else {
		this.getLogger()(msg, level, obj);
	}

}

Log.DEBUG = 1;
Log.INFO = 2;
Log.WARN = 3;
Log.ERROR = 4;
Log.FATAL = 5;
Log.NONE = 6;

/**
 * Static alert logger method. This logger will display a javascript alert
 * (messagebox) with the message.
 * 
 * @param {String}
 *            msg The message to display
 * @param level
 *            The priority level of this log event
 */
Log.alertLogger = function(msg, level) {
	alert(level + " - " + msg);
}
/**
 * Static write logger method. This logger will print the message out to the web
 * page using document.writeln.
 * 
 * @param {String}
 *            msg The message to display
 * @param level
 *            The priority level of this log event
 */
Log.writeLogger = function(msg, level) {
	document.writeln(level + "&nbsp;-&nbsp;" + msg + "<br/>");
}

/**
 * Static Safari WebKit console logger method. This logger will write messages
 * to the Safari javascript console, if available. If this browser doesn't have
 * a javascript console (IE/Moz), then it degrades gracefully to
 * {@link Log#popupLogger}
 * 
 * @param {String}
 *            msg The message to display
 * @param level
 *            The priority level of this log event
 * @param {Log}
 *            obj The originating {@link Log} object.
 */
Log.consoleLogger = function(msg, level, obj) {
	if (window.console) {
		window.console.log(level + " - " + msg);
	} else {
		Log.popupLogger(msg, level, obj);
	}
}

/**
 * Static popup logger method. This logger will popup a new window (if
 * necessary), and add the log message to the end of a list.
 * 
 * @param {String}
 *            msg The message to display
 * @param level
 *            The priority level of this log event
 * @param {Log}
 *            obj The originating {@link Log} object.
 */
Log.popupLogger = function(msg, level, obj) {
	if (obj.popupBlocker) {
		return;
	}
	if (!obj._window || !obj._window.document) {
		obj._window = window
				.open("", 'logger_popup_window',
						'width=420,height=320,scrollbars=1,status=0,toolbars=0,resizable=1');
		if (!obj._window) {
			obj.popupBlocker = true;
			alert("You have a popup window manager blocking the log4js log popup display.\n\nThis must be disabled to properly see logged events.");
			return;
		}
		if (!obj._window.document.getElementById('loggerTable')) {
			obj._window.document
					.writeln("<table width='100%' id='loggerTable'><tr><th align='left'>Time</th><th width='100%' colspan='2' align='left'>Message</th></tr></table>");
			obj._window.document.close();
		}
	}
	var tbl = obj._window.document.getElementById("loggerTable");
	var row = tbl.insertRow(-1);

	var cell_1 = row.insertCell(-1);
	var cell_2 = row.insertCell(-1);
	var cell_3 = row.insertCell(-1);

	var d = new Date();
	var h = d.getHours();
	if (h < 10) {
		h = "0" + h;
	}
	var m = d.getMinutes();
	if (m < 10) {
		m = "0" + m;
	}
	var s = d.getSeconds();
	if (s < 10) {
		s = "0" + s;
	}
	var date = (d.getMonth() + 1) + "/" + d.getDate() + "/" + d.getFullYear()
			+ "&nbsp;-&nbsp;" + h + ":" + m + ":" + s;

	cell_1.style.fontSize = "8pt";
	cell_1.style.fontWeight = "bold";
	cell_1.style.paddingRight = "6px";

	cell_2.style.fontSize = "8pt";

	cell_3.style.fontSize = "8pt";
	cell_3.style.whiteSpace = "nowrap";
	cell_3.style.width = "100%";

	if (tbl.rows.length % 2 == 0) {
		cell_1.style.backgroundColor = "#eeeeee";
		cell_2.style.backgroundColor = "#eeeeee";
		cell_3.style.backgroundColor = "#eeeeee";
	}

	cell_1.innerHTML = date
	cell_2.innerHTML = level;
	cell_3.innerHTML = msg;
}

/**
 * This method is a utility function that takes an object and creates a string
 * representation of it's members.
 * 
 * @param {Object}
 *            the Object that you'd like to see
 * @return {String} a String representation of the object passed
 */
Log.dumpObject = function(obj, indent) {
	if (!indent) {
		indent = "";
	}
	if (indent.length > 20) {
		return;
	} // don't go too far...
	var s = "{\n";
	for (var p in obj) {
		s += indent + p + ":";
		var type = typeof(obj[p]);
		type = type.toLowerCase();
		if (type == 'object') {
			s += Log.dumpObject(obj[p], indent + "----");
		} else {
			s += obj[p];
		}
		s += "\n";
	}
	s += indent + "}";
	return s;
}

logger = new Log(Log.NONE, Log.popupLogger);
